#include "DataFilter.h"
#include "GLTFParser.h"
#include "GeoUtil.h"

void DataFilter::updateCamera(const Mat& viewMatrix, const Mat& projectionMatrix) {
	if (viewMatrix == Mat_Zero || projectionMatrix == Mat_Zero)
		return;
	m_viewMatrix = viewMatrix;
	m_projectionMatrix = projectionMatrix;

	const auto& SkylineY = Config::View3D_SkylineFromTopPx;
	auto view3DSkylineLeft = mapTo3DXZPlane(0, SkylineY);
	auto view3DSkylineRight = mapTo3DXZPlane(W3D, SkylineY);
	auto view3DBottomLeft = mapTo3DXZPlane(0, H3D);
	auto view3DBottomRight = mapTo3DXZPlane(W3D, H3D);

	m_3DRenderAreaMap.insert(SkyLeft, view3DSkylineLeft);
	m_3DRenderAreaMap.insert(SkyRight, view3DSkylineRight);
	m_3DRenderAreaMap.insert(BottomLeft, view3DBottomLeft);
	m_3DRenderAreaMap.insert(BottomRight, view3DBottomRight);

	m_isCameraValid = true;

	LogI << "************************************************************************************";
	LogI << "Skyline-Left (x:" << view3DSkylineLeft.x << ",z:" << view3DSkylineLeft.z << ")"
		 << ", Skyline-Right (x:" << view3DSkylineRight.x << ",z:" << view3DSkylineRight.z << ")";
	LogI << "Bottom-Left (x:" << view3DBottomLeft.x << ",z:" << view3DBottomLeft.z << ")"
		 << ", Bottom-Right (x:" << view3DBottomRight.x << ",z:" << view3DBottomRight.z << ")";
}

/**
 *@warning id = 0 is filtered!
 *@return true: data is valid; false: data is invalid
 */
bool DataFilter::checkValid(const ModelArg& modelArg) {
	if (IsProject_KT) {
		if (!modelArg.identified || modelArg.probability <= 0 || modelArg.ID <= 0 || modelArg.width <= 0 || modelArg.length <= 0)
			return false;
	}
	if (isOutOfRenderArea(modelArg.pos))
		return false;
	return true;
}

bool DataFilter::checkValid(LineArg& lineArg) {
	if (!lineArg.identified || lineArg.probability <= 0 || lineArg.eq.zStart <= lineArg.eq.zEnd)
		return false;
	if (isOutOfRenderArea(lineArg.eq))
		return false;
	recalculateEqStartEnd(lineArg.eq);
	return true;
}

bool DataFilter::isOutOfRenderArea(const P& pos) {
	if (pos.z < skyLeftP().z || pos.z > bottomLeftP().z)
		return true;
	if (pos.x < 0)
		return GeoUtil::checkIfOnRayCCWSweepingSide(pos, bottomLeftP(), skyLeftP());
	else
		return !GeoUtil::checkIfOnRayCCWSweepingSide(pos, bottomRightP(), skyRightP());
}

bool DataFilter::isOutOfRenderArea(const LineEq& eq) {
	if (eq.zStart <= skylineZ() || eq.zEnd >= bottomZ())
		return true;
	return false;
}

void DataFilter::recalculateEqStartEnd(LineEq& eq) {// call it before lineEq datas to be used
	bool cutOffByStart = Config::Line_CutOff_ByEqStart;
	bool cutOffByEnd = Config::Line_CutOff_ByEqEnd;
	const auto& ZhicheInfo = GLTFParser::info(ModelType::Zhiche);
	const auto ZhicheFrontZ = -(ZhicheInfo.length - ZhicheInfo.maxZ);
	if (eq.zStart < ZhicheFrontZ)// should ignore cutoff-status, always cutoff
		cutOffByStart = true;

	const float VOFFSET = 0.1;
	const auto ZBottom = bottomZ();
	const auto ZTop = skylineZ();
	if (!cutOffByStart || (cutOffByStart && eq.zStart > ZBottom))
		eq.zStart = ZBottom + VOFFSET;
	if (!cutOffByEnd || (cutOffByEnd && eq.zEnd < ZTop))
		eq.zEnd = ZTop;
}

P DataFilter::mapTo3DXZPlane(int screenX, int screenY) {
	return GeoUtil::mapTo3DXZPlane(screenX, screenY, W3D, H3D, m_viewMatrix, m_projectionMatrix);
}